home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995…tember: Reference Library / Dev.CD Sep 95 RL / Dev.CD Sep 95 RL.toast / mac / Technical Documentation / develop / develop Issue 21 code / Custom GX Printer Drivers / CustomWriter GX 1.0.1 / CustomBufferingAndIO.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-03  |  26.1 KB  |  988 lines  |  [TEXT/MPS ]

  1. /*
  2.     FILENAME
  3.         CustomBufferingAndIO.c
  4.  
  5.     DESCRIPTION
  6.         Contains code that shows how to implement custom buffering and
  7.         non-standard communications code in a QuickDraw GX printer
  8.         driver.  In this code, we write everything to disk (through our
  9.         own buffers) as an extended version 2 PICT file.
  10.  
  11.     COPYRIGHT
  12.         Copyright © 1995 Apple Computer, Inc.
  13.         All rights reserved.
  14.     
  15.     Modification history
  16.         05/03/95 - Dave Hersey -    Version 1.0.1 fixes the following bugs
  17.                                     in this file.
  18.                                     
  19.                                     * endPict opcodes not always word aligned
  20.                                     * current buffer not being reset on flush
  21.                                     * gxWriteData override could not handle a
  22.                                       simple flush by passing a nil data or
  23.                                       length parameter.
  24.                                     * gxFinishSendPage, gxCloseConnection, and
  25.                                       gxCleanupOpenConnection overrides were
  26.                                       doing work before forwarding their
  27.                                       messages which should have been done after
  28.                                       the forwards. 
  29.  
  30.         01/14/95 - Dave Hersey -    Spawned.
  31.  
  32.     NOTE: Relevant goodies are listed in MPW's "Mark" menu.
  33.  
  34. */
  35.  
  36. #include "CommonDefines.h"
  37.  
  38.  
  39. /*    -----------------------------------------------------------------------
  40.  
  41.     SD_OpenConnection is an override for gxOpenConnection.  In this routine
  42.     we allocate our custom buffer structures and forward the message to the
  43.     default gxOpenConnection handler.  Since we specify customIO in our
  44.     'iobm' resource, no connection is actually opened.
  45.  
  46.     -----------------------------------------------------------------------    */
  47.  
  48. OSErr SD_OpenConnection()
  49. {
  50.     OSErr    anErr;
  51.     
  52.     // Create our buffers, then forward the message.
  53.  
  54.     anErr = CreateBuffers();
  55.     
  56.     if (!anErr)
  57.         anErr = Forward_GXOpenConnection();
  58.     else
  59.         GXCleanupOpenConnection();
  60.  
  61.     return anErr;
  62. }
  63.  
  64.  
  65. /*    -----------------------------------------------------------------------
  66.  
  67.     SD_CloseConnection is an override for gxCloseConnection.  In this
  68.     routine we forward the message to the default gxCloseConnection
  69.     handler and dispose of our custom buffer structures.
  70.  
  71.     -----------------------------------------------------------------------    */
  72.  
  73. OSErr SD_CloseConnection()
  74. {
  75.     OSErr    anErr;
  76.     
  77.     // Forward the message and dispose of our buffers.
  78.  
  79.     anErr = Forward_GXCloseConnection();
  80.     DisposeBuffers();
  81.  
  82.     return anErr;
  83. }
  84.  
  85.  
  86. /*    -----------------------------------------------------------------------
  87.  
  88.     SD_CleanupOpenConnection is an override for gxCleanupOpenConnection.
  89.     In this routine we forward the message and dispose of our data
  90.     structures that might have been allocated before we got an error in
  91.     our gxOpenConnection code.
  92.  
  93.     -----------------------------------------------------------------------    */
  94.  
  95. void SD_CleanupOpenConnection()
  96. {
  97.     // Forward the message and dispose of our buffers (if any).
  98.  
  99.     Forward_GXCleanupOpenConnection();
  100.     DisposeBuffers();
  101. }
  102.  
  103.  
  104. /*    -----------------------------------------------------------------------
  105.  
  106.     SD_BufferData is an override for the gxBufferData message.  To support
  107.     our custom buffering code, we perform a total override of this message,
  108.     fill up the next available buffer with the data.  We'll store data and,
  109.     if necessary, flush buffers until all of the passed data has been
  110.     processed.
  111.  
  112.     -----------------------------------------------------------------------    */
  113.  
  114. OSErr SD_BufferData(Ptr data, long length, long bufferOptions)  // only override for custom buffering
  115. {
  116.     OSErr                        anErr = noErr;
  117.     register char                *pData = data;
  118.     register BufferEntryPtr        buffPtr;
  119.     short                        idx, curBuff;
  120.     Boolean                        foundAFreeBuffer;
  121.     long                        dataLeftToStore = length;
  122.     register BufferGroupHdl        buffGrpHdl = (BufferGroupHdl) GetMessageHandlerInstanceContext();
  123.     char    hdlState;
  124.  
  125.     #pragma unused(bufferOptions);
  126.  
  127.     // Lock the buffer group.
  128.  
  129.     hdlState = HGetState((Handle) buffGrpHdl);
  130.     HLockHi((Handle) buffGrpHdl);
  131.     
  132.     
  133.     // While there's no error, and more data to store, process data.
  134.  
  135.     while (!anErr && (dataLeftToStore > 0))
  136.     {
  137.         // Fill the current buffer with data and indicate that the buffer is dirty.
  138.  
  139.         curBuff = (*buffGrpHdl)->curBuff;
  140.         buffPtr = &(*buffGrpHdl)->buff[curBuff];
  141.  
  142.         while ((dataLeftToStore > 0) && (buffPtr->curOffset < (*buffGrpHdl)->bufferSize))
  143.         {            
  144.             // Move the data to our buffer.
  145.  
  146.             buffPtr->printBuffer.data[buffPtr->curOffset] = *pData;
  147.             ++(buffPtr->curOffset);
  148.             ++pData;
  149.             --dataLeftToStore;
  150.         }
  151.  
  152.         buffPtr->bufferIsDirty = true;
  153.  
  154.  
  155.         // If dataLeftToStore > 0, it means that we've filled the current buffer,
  156.         // and we need another.  Find a free one.
  157.  
  158.         if (dataLeftToStore > 0)
  159.         {
  160.             foundAFreeBuffer = false;
  161.  
  162.             // If we can find another buffer that's not dirty, we've found
  163.             // a buffer we can store data in.  By specifying numBuffers -1
  164.             // below, we avoid checking the current buffer, which we know
  165.             // is  dirty.
  166.         
  167.             for (idx = 0; idx < ((*buffGrpHdl)->numBuffers -1); idx++)
  168.             {
  169.                 // Search up from the last buffer we checked,
  170.                 // wrapping around if necessary.
  171.  
  172.                 ++curBuff;
  173.                 if (curBuff >= (*buffGrpHdl)->numBuffers)
  174.                     curBuff = 0;
  175.                 
  176.                 buffPtr = &(*buffGrpHdl)->buff[curBuff];
  177.  
  178.                 if (!buffPtr->bufferIsDirty)
  179.                 {
  180.                     foundAFreeBuffer = true;
  181.                     break;
  182.                 }
  183.             }
  184.             
  185.             // If there are no free buffers, flush them all and use
  186.             // buffer 0.
  187.  
  188.             if (!foundAFreeBuffer)
  189.             {
  190.                 anErr = FlushBuffers(buffGrpHdl);
  191.                 curBuff = 0;
  192.             }
  193.             
  194.             // Whatever our current buffer is, store it in our buffer
  195.             // structure.
  196.  
  197.             (*buffGrpHdl)->curBuff = curBuff;
  198.         }
  199.     }
  200.  
  201.     // Restore the state of the buffer group handle.
  202.  
  203.     HSetState((Handle) buffGrpHdl, hdlState);
  204.     return anErr;
  205. }
  206.  
  207.  
  208. /*    -----------------------------------------------------------------------
  209.  
  210.     SD_FreeBuffer is an override for the gxFreeBuffer message.  You only
  211.     need to override this message if you're doing asynchronous I/O.  Since
  212.     we're not, this is only here as a reminder for you.  It does nothing
  213.     but forward the message to the default implementation of gxFreeBuffer.
  214.  
  215.     -----------------------------------------------------------------------    */
  216.  
  217. OSErr SD_FreeBuffer(gxPrintingBuffer *theBuffer)
  218. {
  219.     return Forward_GXFreeBuffer(theBuffer);
  220. }
  221.  
  222.  
  223. /*    -----------------------------------------------------------------------
  224.  
  225.     WriteFileData is a routine that writes the passed data to a file whose
  226.     reference ID is stored in our global data.
  227.  
  228.     -----------------------------------------------------------------------    */
  229.  
  230. OSErr WriteFileData(void *dataBuffer, long dataLength)
  231. {
  232.     long                amtToWrite = dataLength;
  233.     DriverGlobalsHdl    drvrGlobalsHdl = GetDriverGlobals();
  234.  
  235.     // Write some data to our file.
  236.  
  237.     return FSWrite((*drvrGlobalsHdl)->curFileRefNum, &amtToWrite, dataBuffer); 
  238. }
  239.  
  240.  
  241. /*    -----------------------------------------------------------------------
  242.  
  243.     SD_WriteData is an override for gxWriteData.  Here, we flush our custom
  244.     buffers and then write the passed data, if any, to the output file
  245.     using our custom I/O routine, WriteFileData.
  246.  
  247.     -----------------------------------------------------------------------    */
  248.  
  249. OSErr SD_WriteData(Ptr data, long length)
  250. {
  251.     OSErr                        anErr;
  252.     register BufferGroupHdl        buffGrpHdl = (BufferGroupHdl) GetMessageHandlerInstanceContext();
  253.  
  254.     // Flush the buffers, then do the actual write.
  255.  
  256.     anErr = FlushBuffers(buffGrpHdl);
  257.  
  258.     if (!anErr && (data != nil) && (length > 0))
  259.         anErr = WriteFileData(data, length);
  260.  
  261.     return anErr;
  262. }
  263.  
  264.  
  265. /*    -----------------------------------------------------------------------
  266.  
  267.     SD_DumpBuffer is an override for gxDumpBuffer.  Here, we write the
  268.     passed data to the output file using our custom I/O routine,
  269.     WriteFileData.
  270.  
  271.     -----------------------------------------------------------------------    */
  272.  
  273. OSErr SD_DumpBuffer(gxPrintingBuffer *theBuffer)
  274. {
  275.     if (theBuffer->size != 0)
  276.         return WriteFileData(theBuffer->data, theBuffer->size);
  277.     else
  278.         return noErr;
  279. }
  280.  
  281.  
  282. /*    -----------------------------------------------------------------------
  283.  
  284.     SD_ImageJob is an override for gxImageJob.  Here, we create and
  285.     initialize global data that we'll use during imaging.
  286.  
  287.     -----------------------------------------------------------------------    */
  288.  
  289. OSErr SD_ImageJob(gxSpoolFile theSpoolFile, long *closeOptions)
  290. {
  291.     OSErr                anErr;
  292.     FSSpec                anFSSpec;
  293.     short                vRefNum;
  294.     long                dirID, itemSize;
  295.     gxJobInfo            jobInfo;
  296.     Str255                fileName;
  297.     DriverGlobalsHdl    drvrGlobalsHdl;
  298.     
  299.     // Create and store a handle for our global data.
  300.  
  301.     anErr = CreateAndStoreGlobals(&drvrGlobalsHdl);
  302.  
  303.  
  304.     // Now, do some more initialization for the first file we'll write to.
  305.  
  306.     if (!anErr)
  307.     {
  308.         // Determine the name of this document, and append ", Page " to it.
  309.         // We'll use this when we create PICT files.  Again, the string
  310.         // should really come from a resource file, for localization
  311.         // reasons.
  312.  
  313.         itemSize = sizeof(gxJobInfo);
  314.         anErr = GetCollectionItem(GXGetJobCollection(GXGetJob()),
  315.                                   gxJobTag, gxPrintingTagID,
  316.                                   &itemSize, &jobInfo);
  317.  
  318.         nrequire(anErr, GetCollectionItemFailed);
  319.  
  320.         if (jobInfo.documentName[0] > 22)    // If necessary, crop file name so we
  321.             jobInfo.documentName[0] = 22;    // have room for our page # stuff.
  322.             
  323.         BlockMove(&jobInfo.documentName[1], &fileName[1], (long) jobInfo.documentName[0]);
  324.         BlockMove(", Page ", &fileName[jobInfo.documentName[0] +1], 7);
  325.  
  326.         // Now make an FSSpec for this file.
  327.  
  328.         FindFolder(kOnSystemDisk, kDesktopFolderType, kCreateFolder, &vRefNum, &dirID);
  329.         fileName[0] = jobInfo.documentName[0] +7;
  330.         FSMakeFSSpec(vRefNum, dirID, fileName, &anFSSpec);
  331.         BlockMove(&anFSSpec, &(*drvrGlobalsHdl)->fileLocation, sizeof(FSSpec));
  332.  
  333.         // Set the number of pages we've imaged to 0.
  334.  
  335.         (*drvrGlobalsHdl)->pagesImaged = 0;
  336.  
  337.         // With our global data initialized, it's time to forward the
  338.         // gxImageJob message so that the job is processed.
  339.  
  340.         anErr = Forward_GXImageJob(theSpoolFile, closeOptions);
  341.  
  342.         // All done imaging!  Close the last file, if it's still open,
  343.         // and flush the volume so that we get the changes out to disk
  344.         // faster.
  345.  
  346.         if ((*drvrGlobalsHdl)->curFileRefNum)
  347.             FSClose((*drvrGlobalsHdl)->curFileRefNum);
  348.     
  349.         FlushVol("\p", (*drvrGlobalsHdl)->fileLocation.vRefNum);
  350.         
  351. GetCollectionItemFailed:
  352.  
  353.         // However we get here, dispose of the global data we allocated above.
  354.  
  355.         DisposeGlobals();
  356.     }
  357.  
  358.     return anErr;
  359. }
  360.  
  361.  
  362. /*    -----------------------------------------------------------------------
  363.  
  364.     SD_StartSendPage is an override for gxStartSendPage.  Here, we close
  365.     the current file (if any) and open a new one for the next page.  This
  366.     way, we're able to create a PICT file for each page of a document.
  367.  
  368.     -----------------------------------------------------------------------    */
  369.  
  370. OSErr SD_StartSendPage(gxFormat pageFormat)
  371. {
  372.     OSErr                anErr;
  373.     char                numStr[11], maxChars, nextChar;
  374.     long                numLen;
  375.     DriverGlobalsHdl    drvrGlobalsHdl = GetDriverGlobals();
  376.  
  377. #pragma unused(pageFormat);
  378.  
  379.     // Close current file, if any.
  380.  
  381.     HLockHi((Handle) drvrGlobalsHdl);
  382.  
  383.     if ((*drvrGlobalsHdl)->curFileRefNum)
  384.     {
  385.         FSClose((*drvrGlobalsHdl)->curFileRefNum); 
  386.         (*drvrGlobalsHdl)->curFileRefNum = nil;
  387.     }
  388.     
  389.     // Bump the number of pages imaged, and use that in the
  390.     // filename for the next page.  We started pagesImaged at
  391.     // 0, so the first filename will have a 1 appended to it.
  392.  
  393.     (*drvrGlobalsHdl)->lastYPos = 0;
  394.     ++(*drvrGlobalsHdl)->pagesImaged;
  395.  
  396.     NumToString((*drvrGlobalsHdl)->pagesImaged, numStr);
  397.     numLen = (unsigned long) numStr[0];
  398.     maxChars = 31 -numLen; 
  399.  
  400.     if ((*drvrGlobalsHdl)->fileLocation.name[0] > maxChars)
  401.         (*drvrGlobalsHdl)->fileLocation.name[0] = maxChars;
  402.  
  403.     nextChar = (*drvrGlobalsHdl)->fileLocation.name[0] +1;
  404.     BlockMove(&numStr[1], &(*drvrGlobalsHdl)->fileLocation.name[nextChar], numLen);
  405.     (*drvrGlobalsHdl)->fileLocation.name[0] += numLen;
  406.  
  407.  
  408.     // Create a new file for this page, recklessly replacing any
  409.     // existing file with that name.
  410.  
  411.     FSpDelete(&(*drvrGlobalsHdl)->fileLocation);
  412.     anErr = FSpCreate(&(*drvrGlobalsHdl)->fileLocation, kFileCreator, kFileType, smSystemScript); 
  413.  
  414.  
  415.     // Open the new file, and write the beginning of the PICT file.
  416.  
  417.     if (!anErr)
  418.         anErr = FSpOpenDF(&(*drvrGlobalsHdl)->fileLocation, fsCurPerm, &(*drvrGlobalsHdl)->curFileRefNum);
  419.  
  420.     if (!anErr)
  421.         anErr = WriteStartOfPICTFile();
  422.  
  423.  
  424.     // Initialize this page's PICT's pixmap's bounds to (0,0,0,0).
  425.  
  426.     (*drvrGlobalsHdl)->pixMapBounds.top = 0;
  427.     (*drvrGlobalsHdl)->pixMapBounds.left = 0;
  428.     (*drvrGlobalsHdl)->pixMapBounds.bottom = 0;
  429.     (*drvrGlobalsHdl)->pixMapBounds.right = 0;
  430.  
  431.  
  432.     // Strip the page number we just added from the file name in our globals.
  433.     // This way, we won't have it when it comes time to set up the file name
  434.     // for the next page.
  435.  
  436.     (*drvrGlobalsHdl)->fileLocation.name[0] -= numLen;
  437.     HUnlock((Handle) drvrGlobalsHdl);
  438.  
  439.  
  440.     // Finally, forward gxStartSendPage so that GX and any overrides can do
  441.     // whatever they need to do.
  442.  
  443.     if (!anErr)
  444.         anErr = Forward_GXStartSendPage(pageFormat);
  445.  
  446.     return anErr;
  447. }
  448.  
  449.  
  450. /*    -----------------------------------------------------------------------
  451.  
  452.     SD_FinishSendPage is an override for gxFinishSendPage.  Here, we
  453.     forward the gxFinishPage message and write out the end of the PICT
  454.     file we created in SD_StartSendPage.
  455.  
  456.     -----------------------------------------------------------------------    */
  457.  
  458. OSErr SD_FinishSendPage()
  459. {
  460.     OSErr    anErr1, anErr2;
  461.     
  462.     // Forward the gxFinishPage message and write out the
  463.     // remainder of this page's PICT file.
  464.  
  465.     anErr1 = Forward_GXFinishSendPage();
  466.     anErr2 = WriteEndOfPICTFile();
  467.  
  468.     return (anErr1)? anErr1: anErr2;
  469. }
  470.  
  471.  
  472. /*    -----------------------------------------------------------------------
  473.  
  474.     SD_SetupImageData is an override for gxSetupImageData.  Here, we
  475.     forward the gxSetupImageData message and store the resolution for
  476.     the PICTs we'll create.
  477.  
  478.     -----------------------------------------------------------------------    */
  479.  
  480. OSErr SD_SetupImageData(gxRasterImageDataHdl hImageData)
  481. {
  482.     OSErr                anErr;
  483.     DriverGlobalsHdl    drvrGlobalsHdl = GetDriverGlobals();
  484.  
  485.     // Do the default setup.
  486.  
  487.     anErr = Forward_GXSetupImageData(hImageData);
  488.      
  489.     // Set up our PICT resolution based on the information in the
  490.     // gxRasterImageDataHdl that we're passed.
  491.  
  492.     (*drvrGlobalsHdl)->hRes = (*hImageData)->hImageRes;
  493.     (*drvrGlobalsHdl)->vRes = (*hImageData)->vImageRes;
  494.  
  495.     return (anErr);
  496. }
  497.  
  498.  
  499. /*    -----------------------------------------------------------------------
  500.  
  501.     CreateBuffers is the routine which creates and initializes our custom
  502.     buffers.
  503.  
  504.     -----------------------------------------------------------------------    */
  505.  
  506. OSErr CreateBuffers()
  507. {
  508.     OSErr            anErr;
  509.     BufferGroupHdl    theBuffs;
  510.     long            thisBig = sizeof(BufferGroup) +(sizeof(BufferEntry) * (kNumBuffs -1));
  511.     
  512.     // Create one handle for our buffers.  Note that this code does not
  513.     // reduce the buffer size based on available memory.  That would be
  514.     // a preferable approach.
  515.  
  516.     theBuffs = (BufferGroupHdl) TempNewHandle(thisBig, &anErr);
  517.  
  518.     if (!anErr)
  519.     {
  520.         short    idx;
  521.  
  522.         // Initialize the buffer structure and store the buffer group in
  523.         // our instance context so we can get it later.
  524.     
  525.         (*theBuffs)->numBuffers = kNumBuffs;
  526.         (*theBuffs)->bufferSize = kOurBufferSize;
  527.         (*theBuffs)->curBuff = 0;
  528.         
  529.         for (idx = 0; idx < (*theBuffs)->numBuffers; idx++)
  530.         {
  531.             (*theBuffs)->buff[idx].bufferIsDirty = false;
  532.             (*theBuffs)->buff[idx].curOffset = 0;
  533.         }
  534.  
  535.         SetMessageHandlerInstanceContext(theBuffs);
  536.     }
  537.     else;
  538.         // We suck, we didn't downsize our buffers for the available RAM,
  539.         // and now we've failed miserably.
  540.  
  541.     return anErr;
  542. }
  543.  
  544.  
  545. /*    -----------------------------------------------------------------------
  546.  
  547.     DisposeBuffers is the routine which disposes of our custom buffers.
  548.  
  549.     -----------------------------------------------------------------------    */
  550.  
  551. OSErr DisposeBuffers()
  552. {
  553.     Handle    bufferGroup = GetMessageHandlerInstanceContext();
  554.  
  555.     // Have we set up them there buffers?  Then erradicate 'em.
  556.  
  557.     if (bufferGroup != nil)
  558.         DisposeHandle(bufferGroup);
  559.  
  560.     return noErr;
  561. }
  562.  
  563.  
  564. /*    -----------------------------------------------------------------------
  565.  
  566.     FlushBuffers is the routine which flushes our custom buffers.  To do
  567.     this, it cycles through each one to see if there's data in it.  If so,
  568.     it sends gxDumpBuffer for the buffer, followed by gxFreeBuffer.
  569.  
  570.     -----------------------------------------------------------------------    */
  571.  
  572. OSErr FlushBuffers(BufferGroupHdl buffGrpHdl)
  573. {
  574.     OSErr    anErr = noErr;
  575.     short    idx;
  576.     char    hdlState;
  577.  
  578.     // Lock the buffer group.
  579.  
  580.     hdlState = HGetState((Handle) buffGrpHdl);
  581.     HLockHi((Handle) buffGrpHdl);
  582.  
  583.     // Send gxDumpBuffer and gxFreeBuffer for each buffer with data in it.
  584.  
  585.     for (idx = 0; idx < (*buffGrpHdl)->numBuffers; idx++)
  586.     {
  587.         if ((*buffGrpHdl)->buff[idx].bufferIsDirty)
  588.         {
  589.             // gxDumpBuffer will look at printbuffer.size to see how much
  590.             // data to write out.  Set it to the current offset, which just
  591.             // happens to equal the number of bytes we've stored in the buffer.
  592.  
  593.             (*buffGrpHdl)->buff[idx].printBuffer.size = (*buffGrpHdl)->buff[idx].curOffset;
  594.  
  595.  
  596.             // Send gxDumpBuffer and gxFreeBuffer for this buffer.
  597.  
  598.             anErr = Send_GXDumpBuffer(&(*buffGrpHdl)->buff[idx].printBuffer);
  599.  
  600.             if (!anErr)
  601.                 anErr = Send_GXFreeBuffer(&(*buffGrpHdl)->buff[idx].printBuffer);
  602.  
  603.  
  604.             // Clear the buffer's dirty flag and reset its data offset.
  605.  
  606.             (*buffGrpHdl)->buff[idx].bufferIsDirty = false;
  607.             (*buffGrpHdl)->buff[idx].curOffset = 0;
  608.         }
  609.     }
  610.     
  611.     (*buffGrpHdl)->curBuff = 0;
  612.  
  613.     // Restore the state of the buffer group handle.
  614.     HSetState((Handle) buffGrpHdl, hdlState);
  615.     
  616.     return anErr;
  617. };
  618.  
  619.  
  620. /*    -----------------------------------------------------------------------
  621.  
  622.     WriteStartOfPICTFile is a routine that writes out the start of an
  623.     extended version 2 PICT that only contains a clip and a pixmap.  In
  624.     this routine we write out all the data up to the pixmap data.  However,
  625.     since we won't know things like the bounds and rowbytes until later,
  626.     we simply write out zeroes now, and update everything when we're done
  627.     imaging this page.  We do the updating in the routine
  628.     WriteEndOfPICTFile.
  629.  
  630.     -----------------------------------------------------------------------    */
  631.  
  632. OSErr WriteStartOfPICTFile()
  633. {
  634.     OSErr    anErr;
  635.     Ptr        dataPtr;
  636.  
  637.     // We need to write out the 512 unused bytes that every PICT file
  638.     // contains, plus 122 bytes that comprise the header and information
  639.     // that precedes the pixel data in our pixmap.  Since we do this at
  640.     // the start of the page, when SD_DumpBuffer and SD_WriteData write
  641.     // data to the file, they'll actually be filling out the pixel data
  642.     // in our PICT's pixmap.
  643.     //
  644.     // Anyway, create a clear pointer that's big enough to hold all the
  645.     // data that precedes our pixmap's pixels.  If there are no errors,
  646.     // write it out via our gxBufferData override.  When done, dispose
  647.     // of the pointer we used.
  648.  
  649.     dataPtr = NewPtrClear(512 +122);
  650.  
  651.     if (!(anErr = MemError()))
  652.     {
  653.         anErr = Send_GXBufferData(dataPtr, 512 +122, gxNoBufferOptions);
  654.         DisposePtr(dataPtr);
  655.     }
  656.  
  657.     return anErr;
  658. }
  659.  
  660.  
  661. /*    -----------------------------------------------------------------------
  662.  
  663.     WriteEndOfPICTFile is a routine that writes out the end of an
  664.     extended version 2 PICT that only contains a clip and a pixmap.  It
  665.     also updates the beginning of the PICT, with information about the
  666.     pixmap's bounds and so forth.  The beginning of the PICT was written
  667.     out with the WriteStartOfPICTFile routine.
  668.     
  669.     Writing out the end of the PICT simply involves writing out the end-
  670.     of-PICT opCode 0x00FF.  Most of the work done in this routine has to
  671.     do with updating the start-of-PICT data that we already wrote out.
  672.     
  673.     -----------------------------------------------------------------------    */
  674.  
  675. OSErr WriteEndOfPICTFile()
  676. {
  677.     OSErr                anErr;
  678.     fixed                hRes, vRes, hResScale, vResScale;
  679.     short                finalPictData;
  680.     short                initialPictData[61], curFileRefNum;
  681.     long                index = 0, pictSize;
  682.     Rect                drawRect, a72dpiDrawRect;
  683.     DriverGlobalsHdl    drvrGlobalsHdl = GetDriverGlobals();
  684.  
  685.     // Get the horizontal and vertical resolutions that we imaged
  686.     // at, along with the scaling necessary to apply to 72 dpi
  687.     // viewing rectangles for this image.
  688.  
  689.     hRes = (*drvrGlobalsHdl)->hRes;
  690.     vRes = (*drvrGlobalsHdl)->vRes;
  691.     hResScale = FixRatio(hRes >>16, 72);
  692.     vResScale = FixRatio(vRes >>16, 72);
  693.  
  694.     // Get the pixmap's native bounds, and calculate the correct
  695.     // viewing rectangle for 72 dpi.  We pin the top/left corner
  696.     // of each rectangle to 0,0 for convenience.
  697.  
  698.     drawRect = (*drvrGlobalsHdl)->pixMapBounds;
  699.     OffsetRect(&drawRect, -drawRect.left, -drawRect.top);
  700.     a72dpiDrawRect.top = a72dpiDrawRect.left = 0;
  701.     a72dpiDrawRect.bottom = (FixMul(ff(drawRect.bottom), vResScale) >>16);
  702.     a72dpiDrawRect.right = (FixMul(ff(drawRect.right), hResScale) >>16);
  703.  
  704.     // Flush our buffers. (Yes, since we're doing our own buffer
  705.     // management, we could just call FlushBuffers, but this is how
  706.     // you'd do it otherwise.)
  707.  
  708.     anErr = Send_GXWriteData(nil, 0);
  709.     nrequire(anErr, FlushBuffersFailed);
  710.  
  711.     // Get the current file's refNum.  Since we flushed the buffers,
  712.     // we can go back and see how big the PICT is.  Do so.
  713.  
  714.     curFileRefNum = (*drvrGlobalsHdl)->curFileRefNum;
  715.     anErr = GetEOF(curFileRefNum, &pictSize);
  716.     nrequire(anErr, GetEOFFailed);
  717.  
  718.     // If necessary, add a $00 to word align the endPict opcode.
  719.     
  720.     if ((pictSize & 1) != 0)
  721.     {
  722.         finalPictData = 0;
  723.         anErr = Send_GXWriteData((Ptr) &finalPictData, 1);
  724.         nrequire(anErr, WordAlignFailed);
  725.         ++pictSize;
  726.     }
  727.  
  728.     // Write the endPict opcode.
  729.  
  730.     finalPictData = 0x00FF;
  731.     anErr = Send_GXWriteData((Ptr) &finalPictData, 2);
  732.     nrequire(anErr, SendFinalDataFailed);
  733.  
  734.     // Now, we're ready to update the information that precedes
  735.     // the pixmap data.  Set our file position to 512 bytes from
  736.     // the start.  That puts us just past the obsolete PICT header
  737.     // that we're required to include.
  738.  
  739.     anErr = SetFPos(curFileRefNum, fsFromStart, 512);
  740.     nrequire(anErr, SetFPosFailed);
  741.  
  742.     // Store the low word of picsize.
  743.  
  744.     initialPictData[index] = pictSize & 0xFFFF;
  745.     ++index;
  746.  
  747.     // Store the 72 dpi picFrame.
  748.  
  749.     initialPictData[index] = a72dpiDrawRect.top;
  750.     ++index;
  751.     initialPictData[index] = a72dpiDrawRect.left;
  752.     ++index;
  753.     initialPictData[index] = a72dpiDrawRect.bottom;
  754.     ++index;
  755.     initialPictData[index] = a72dpiDrawRect.right;
  756.     ++index;
  757.  
  758.     // Store the pict version opcode ($0011).
  759.  
  760.     initialPictData[index] = 0x0011;
  761.     ++index;
  762.  
  763.     // Store the pict version ($02FF).
  764.  
  765.     initialPictData[index] = 0x02FF;
  766.     ++index;
  767.  
  768.     // Store the header opcode ($0C00).
  769.  
  770.     initialPictData[index] = 0x0C00;
  771.     ++index;
  772.  
  773.     // Store the header version (-2).
  774.  
  775.     initialPictData[index] = -2;
  776.     ++index;
  777.  
  778.     // Store the reserved word (0).
  779.  
  780.     initialPictData[index] = 0;
  781.     ++index;
  782.  
  783.     // Store the native horizontal resolution (fixed) of the source data.
  784.  
  785.     initialPictData[index] = hRes >>16;
  786.     ++index;
  787.     initialPictData[index] = hRes & 0xFFFF;
  788.     ++index;
  789.  
  790.     // Store the native vertical resolution (fixed) of the source data.
  791.  
  792.     initialPictData[index] = vRes >>16;
  793.     ++index;
  794.     initialPictData[index] = vRes & 0xFFFF;
  795.     ++index;
  796.  
  797.     // Store the native source rectangle.
  798.  
  799.     initialPictData[index] = drawRect.top;
  800.     ++index;
  801.     initialPictData[index] = drawRect.left;
  802.     ++index;
  803.     initialPictData[index] = drawRect.bottom;
  804.     ++index;
  805.     initialPictData[index] = drawRect.right;
  806.     ++index;
  807.  
  808.     // Store the reserved longword (0L).
  809.  
  810.     initialPictData[index] = 0;
  811.     ++index;
  812.     initialPictData[index] = 0;
  813.     ++index;
  814.  
  815.     // Store the clip opcode (1).
  816.  
  817.     initialPictData[index] = 1;
  818.     ++index;
  819.     
  820.     // Store the size of the clip region data (10).
  821.  
  822.     initialPictData[index] = 10;
  823.     ++index;
  824.  
  825.     // Store the clip rectangle.
  826.  
  827.     initialPictData[index] = drawRect.top;
  828.     ++index;
  829.     initialPictData[index] = drawRect.left;
  830.     ++index;
  831.     initialPictData[index] = drawRect.bottom;
  832.     ++index;
  833.     initialPictData[index] = drawRect.right;
  834.     ++index;
  835.  
  836.     // Store the opBitsRect opcode ($009A).
  837.  
  838.     initialPictData[index] = 0x009A;
  839.     ++index;
  840.  
  841.     // Store the pixmap's baseAddr placeholder (always $000000FF).
  842.  
  843.     initialPictData[index] = 0x0000;
  844.     ++index;
  845.     initialPictData[index] = 0x00FF;
  846.     ++index;
  847.  
  848.     // Store the rowBytes (high bit must be set for a pixmap).
  849.  
  850.     initialPictData[index] = (*drvrGlobalsHdl)->pixMapRowBytes | 0x8000;
  851.     ++index;
  852.  
  853.     // Store the pixmap bounds at source resolution.
  854.  
  855.     initialPictData[index] = drawRect.top;
  856.     ++index;
  857.     initialPictData[index] = drawRect.left;
  858.     ++index;
  859.     initialPictData[index] = drawRect.bottom;
  860.     ++index;
  861.     initialPictData[index] = drawRect.right;
  862.     ++index;
  863.  
  864.     // Store the pixmap version (0).
  865.  
  866.     initialPictData[index] = 0;
  867.     ++index;
  868.  
  869.     // Store the pixmap packing type (2 == we dropped pad bytes).
  870.  
  871.     initialPictData[index] = 2;
  872.     ++index;
  873.  
  874.     // Store the packing size (0L).
  875.  
  876.     initialPictData[index] = 0;
  877.     ++index;
  878.     initialPictData[index] = 0;
  879.     ++index;
  880.  
  881.     // Store the horizontal resolution (fixed) of the source data.
  882.  
  883.     initialPictData[index] = hRes >>16;
  884.     ++index;
  885.     initialPictData[index] = hRes & 0xFFFF;
  886.     ++index;
  887.  
  888.     // Store the vertical resolution (fixed) of the source data.
  889.  
  890.     initialPictData[index] = vRes >>16;
  891.     ++index;
  892.     initialPictData[index] = vRes & 0xFFFF;
  893.     ++index;
  894.  
  895.     // Store the pixelType (direct == 16).
  896.  
  897.     initialPictData[index] = 16;
  898.     ++index;
  899.  
  900.     // Store the pixelSize (32 (packed to 24) bits per pixel).
  901.  
  902.     initialPictData[index] = 32;
  903.     ++index;
  904.  
  905.     // Store the pixel component count (3).
  906.  
  907.     initialPictData[index] = 3;
  908.     ++index;
  909.  
  910.     // Store the pixel component size (8).
  911.  
  912.     initialPictData[index] = 8;
  913.     ++index;
  914.  
  915.     // Store the offset to next plane (0).
  916.  
  917.     initialPictData[index] = 0;
  918.     ++index;
  919.     initialPictData[index] = 0;
  920.     ++index;
  921.  
  922.     // Store the ctSeed (0L).
  923.  
  924.     initialPictData[index] = 0;
  925.     ++index;
  926.     initialPictData[index] = 0;
  927.     ++index;
  928.  
  929.     // Store the ctFlags (0).
  930.  
  931.     initialPictData[index] = 0;
  932.     ++index;
  933.  
  934.     // Store the ctSize (-1 == 0 entries.  Unused for direct pixmaps anyway).
  935.  
  936.     initialPictData[index] = -1;
  937.     ++index;
  938.  
  939.     // Store the source rectangle at source resolution.
  940.  
  941.     initialPictData[index] = drawRect.top;
  942.     ++index;
  943.     initialPictData[index] = drawRect.left;
  944.     ++index;
  945.     initialPictData[index] = drawRect.bottom;
  946.     ++index;
  947.     initialPictData[index] = drawRect.right;
  948.     ++index;
  949.  
  950.     // Store the destination rectangle at 72 dpi.
  951.  
  952.     initialPictData[index] = a72dpiDrawRect.top;
  953.     ++index;
  954.     initialPictData[index] = a72dpiDrawRect.left;
  955.     ++index;
  956.     initialPictData[index] = a72dpiDrawRect.bottom;
  957.     ++index;
  958.     initialPictData[index] = a72dpiDrawRect.right;
  959.     ++index;
  960.  
  961.     // Store the transfer mode (ditherCopy).
  962.  
  963.     initialPictData[index] = ditherCopy;
  964.     ++index;
  965.  
  966.     // The number of bytes we've got to write is index *2 (since
  967.     // we bumped index at every word we stored).  Empirically, this
  968.     // comes out to 122 bytes, which explains the 122 bytes in
  969.     // WriteStartOfPICTFile and the 61 words allocated for
  970.     // initialPictData.  If you change the data that's written into
  971.     // this file, make sure you update those two values!!
  972.     //
  973.     // Write out the updated data.
  974.  
  975.     index *= 2;
  976.     anErr = FSWrite(curFileRefNum, &index, initialPictData); 
  977.  
  978. SetFPosFailed:
  979. SendFinalDataFailed:
  980. WordAlignFailed:
  981. GetEOFFailed:
  982. FlushBuffersFailed:
  983.  
  984.     return anErr;
  985. }
  986.  
  987.  
  988.